package tbs.framework.utils.wrapper

import tbs.framework.interfaces.FunctionWithThrows
import tbs.framework.lock.ILock
import tbs.framework.lock.expections.ObtainLockFailException
import tbs.framework.log.ILogger
import tbs.framework.log.annotations.AutoLogger
import tbs.framework.utils.IWrapper
import tbs.framework.utils.holders.SingletonHolder
import java.time.Duration
import java.util.concurrent.TimeUnit


open class LockWrapper(private val lockTimeOut: Long = 30L, private val lockTimeUnit: TimeUnit = TimeUnit.SECONDS) :
    IWrapper<Any, Any, ILock, Any, Throwable> {

    @AutoLogger
    private var logger: ILogger? = null;

    companion object {
        @JvmStatic
        val instance: LockWrapper get() = SingletonHolder.getInstance<LockWrapper>(LockWrapper::class.java);
    }

    fun quickLock(runable: Runnable, lock: ILock) {
        wrapVoidSafely({ runable }, lock, null);
    }
    

    override fun wrap(
        function: FunctionWithThrows<in Any, in Any, Throwable?>, param: Any?, lk: ILock, arg2: Any?
    ): Any? {
        var result: Any? = null;

        try {
            logger?.trace("try to lock for {}", lk.toString());
            val isLocked = lk.tryLock(
                Duration.of(lockTimeOut, lockTimeUnit.toChronoUnit())
            );
            if (isLocked) {
                logger?.trace("locked for {}", lk.toString());
                result = function.apply(param);
            } else {
                throw ObtainLockFailException("Failed to obtain lock in time");
            }
        } finally {
            logger?.trace("unlock for {}", lk.toString());
            lk.unLock();
        }
        return result;
    }

}
